# coding: utf-8
import datetime
import json
import os

import talib
from PySide6.QtCore import QAbstractTableModel
from PySide6.QtCore import Qt

from enums import CodeKind
from model.market import Market


class ETFModel(QAbstractTableModel):
    def __init__(self):
        super().__init__()
        self.init_data()
        self.money = None

    def init_data(self):
        self.newfile = "data/fund/newest.json"
        self._data = [{"code": "159985", "name": "豆粕ETF"},
                      {"code": "159934", "name": "黄金ETF"},
                      {"code": "159980", "name": "有色ETF"},
                      {"code": "159981", "name": "能源化工ETF"},
                      {"code": "161226", "name": "国投白银LOF"},
                      {"code": "515220", "name": "煤炭ETF"}
                    ]
        self._headData = [{"title": "ETF代码", "name": "code", "type": str},
                          {"title": "名称", "name": "name", "type": str},
                          {"title": "净值日期", "name": "net_date", "type": str},
                          {"title": "净值", "name": "net_value", "type": float},
                          {"title": "状态", "name": "status", "type": str},
                          {"title": "操作", "name": "operate", "type": str},
                          {"title": "数量", "name": "num", "type": int}]

        for etf in self._data:
            for col in self._headData:
                if etf.get(col["name"]) is None:
                    etf[col["name"]] = col["type"]()

        # 日期
        curr_date = datetime.datetime.now()
        three_pm = datetime.datetime(curr_date.year, curr_date.month, curr_date.day, 15)
        curr_date = curr_date if curr_date > three_pm else curr_date - datetime.timedelta(days=1)
        self.curr_date = curr_date  # 最后一个交易日
        self.p1 = 11

        # 读取已经更新的数据
        self.read_file()

    def read_file(self):
        # 读取已经更新的数据
        if os.path.exists(self.newfile):
            with open(self.newfile, "r", encoding="utf-8") as f:
                data = json.load(f)
        else:
            return

        if data["date"] == self.curr_date.strftime("%Y%m%d"):
            self._data = data["data"]

    def rowCount(self, parent=None):
        return len(self._data)

    def columnCount(self, parent=None):
        return len(self._headData)

    def data(self, index, role=None):
        if role == Qt.ItemDataRole.DisplayRole:
            return self._data[index.row()][self._headData[index.column()]["name"]]
        return None

    def headerData(self, section, orientation, role=...):
        if (role == Qt.ItemDataRole.DisplayRole
                and orientation == Qt.Orientation.Horizontal):  # 横向标题
            return self._headData[section]["title"]
        return None

    def flags(self, index):
        return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable

    def refresh(self):
        self.beginResetModel()

        begin_date = self.curr_date - datetime.timedelta(days=self.p1 + 1000)
        begin_date = begin_date.strftime("%Y%m%d")
        end_date = self.curr_date.strftime("%Y%m%d")

        # 判断是否需要买入
        for etf in self._data:
            if etf["status"] == "已更":
                continue

            code = etf["code"]
            name = etf["name"]
            market = Market(code, name, begin_date, end_date, CodeKind.FUND)
            hist = market.market_count_history(end_date, self.p1 + 1000)
            hist = hist[["trade_date", "unit_worth"]]

            last_market = hist.iloc[-1, :]
            last_date = last_market["trade_date"]  # 实际最新日期
            last_close = last_market["unit_worth"]
            # 获取前一个交易日的日期
            fore_date = market.count_trade_date(end_date, 0)    # 理论最新日期
            if last_date < fore_date:  # 场内基金的最新净值日期早于前一交易日，则忽略
                continue

            hist.loc[:, "kama"] = talib.KAMA(hist["unit_worth"], timeperiod=self.p1)
            hist.loc[:, "pre_worth"] = hist["unit_worth"].shift(1)
            hist.loc[:, "pre_kama"] = hist["kama"].shift(1)
            hist.loc[:, "operate"]= hist.apply(lambda x: self.get_operate(x), axis=1)
            operation = hist.iloc[-1, :]["operate"]
            # operation = self.double_average_way(hist)  # 双均线

            etf["net_value"] = str(last_close)
            etf["net_date"] = last_date
            etf["operate"] = operation
            etf["status"] = "已更"
            if operation == "买入":
                etf["num"] = int(float(self.money) / last_close / 100) * 100

            hist = hist[["trade_date", "unit_worth", "kama", "operate"]]
            hist.to_json("data/fund/{}.json".format(code), orient="records")

        self.endResetModel()

        self.write_newest()

    def get_operate(self, x):
        if x["unit_worth"] > x["kama"] and x["pre_worth"] <= x["pre_kama"]:
            return "买入"
        elif x["unit_worth"] < x["kama"] and x["pre_worth"] >= x["pre_kama"]:
            return "卖出"
        else:
            return "观望"

    def double_average_way(self, hist):
        # 只有金叉，才建仓买入
        last_market = hist.iloc[-1, :]
        pre_market = hist.iloc[-2, :]
        pre_ma7 = pre_market["kama"]
        pre_value = pre_market["unit_worth"]
        ma7 = last_market["kama"]
        net_value = last_market["unit_worth"]

        if pre_value <= pre_ma7 and net_value >= ma7:  # 只有符合金叉，才可能建仓买入
            return "买入"
        elif pre_value >= pre_ma7 and net_value <= ma7:  # 死叉，卖出
            return "卖出"
        else:
            return "观望"

    def setMoney(self, money):
        self.money = money

    def get_code_info(self, index):
        if index.isValid():
            code = self._data[index.row()]["code"]
            name = self._data[index.row()]["name"]
            net_date = self._data[index.row()]["net_date"]
            return code, name, net_date
        return None

    def write_newest(self):
        save = {"date": self.curr_date.strftime("%Y%m%d"), "data": self._data}
        with open(self.newfile, "w", encoding="utf-8") as f:
            json.dump(save, f, indent=4, ensure_ascii=False)

    def sort(self, column, order=...):
        self.beginResetModel()
        reverse = None
        if order == Qt.SortOrder.AscendingOrder:
            reverse = False
        elif order == Qt.SortOrder.DescendingOrder:
            reverse = True
        field = self._headData[column]["name"]
        self._data = sorted(self._data, key=lambda x: x[field],reverse=reverse)
        self.endResetModel()


class HistModel(QAbstractTableModel):
    def __init__(self, code: str):
        super().__init__()
        self.init_data(code)

    def init_data(self, code: str):
        self._data = []
        self._headData = [{"title": "净值日期", "name": "trade_date", "type": str},
                          {"title": "净值", "name": "unit_worth", "type": float},
                          {"title": "操作", "name": "operate", "type": str}]

        for etf in self._data:
            for col in self._headData:
                if etf.get(col["name"]) is None:
                    etf[col["name"]] = col["type"]()

        # 读取已经更新的数据
        self.read_file(code)

    def read_file(self, code: str):
        # 读取已经更新的数据
        file = "data/fund/{}.json".format(code)
        if os.path.exists(file):
            with open(file, "r", encoding="utf-8") as f:
                data = json.load(f)
        else:
            return
        self._data = list(data)
        self._data = sorted(self._data, key=lambda x: x["trade_date"], reverse=True)
        self._data = self._data[:365]

    def rowCount(self, parent=None):
        return len(self._data)

    def columnCount(self, parent=None):
        return len(self._headData)

    def data(self, index, role=None):
        if role == Qt.ItemDataRole.DisplayRole:
            return self._data[index.row()][self._headData[index.column()]["name"]]
        return None

    def headerData(self, section, orientation, role=...):
        if (role == Qt.ItemDataRole.DisplayRole
                and orientation == Qt.Orientation.Horizontal):  # 横向标题
            return self._headData[section]["title"]
        return None

    def flags(self, index):
        return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable

    def change_data(self, code):
        self.beginResetModel()
        self.read_file(code)
        self.endResetModel()
